
//解析模块

#include<iostream>
#include<string>
#include<vector>
#include<boost/filesystem.hpp>
#include"util.hpp"
const std::string src_path="data/input";
const std::string output="data/raw_html/raw.txt";
const std::string adver="data/advert/advert.txt";

#include"index.hpp"

typedef struct DocInfo
{
    std::string title;//标题
    std::string content;//文档内容
    std::string url;//文档在官网中的url
}DocInfo_t;

//const &: 输入
//*: 输出
//&：输入输出
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list);
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results);
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output);
// bool Sav(const std::string& adver);
int main()
{
    std::vector<std::string> files_list;
    //第一步: 递归式的把每个html文件名带路径，保存到files_list中，方便后期进行一个一个的文件进行读取
    if(!EnumFile(src_path,&files_list))
    {
        std::cerr << "enum file name error!" << std::endl;
        return 1;
    }
    std::vector<DocInfo_t> results;
    //第二步,对文件解析，存到results
    if(!ParseHtml(files_list,&results))
    {
        std::cerr << "parse html error" << std::endl;
        return 2;
    }
    // //第三步,把解析的放到output下，/3做分隔符
    if(!SaveHtml(results,output))//7
    {
        std::cerr << " sava html error" << std::endl;
        return 3;
    }
    // if(!Sav(adver))//7
    // {
    //     std::cerr << " sava html error" << std::endl;
    //     return 4;
    // }
}

bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)//1
{
    namespace fs=boost::filesystem;
    fs::path root_path(src_path);
    if(!fs::exists(root_path))
    {
        std::cerr<<src_path<<" not exits"<<std::endl;
        return false;
    }
    //定义空的迭代器，判断结束
    fs::recursive_directory_iterator end;
    for(fs::recursive_directory_iterator iter(root_path);iter!=end;iter++)
    {
        if(!fs::is_regular_file(*iter))
        {
            continue;
        }
        //iter->path().extension() != ".html"，这里其实是在进行 path 对象和字符串
        //字面量的比较。Boost.Filesystem 对 path 和字符串的比较做了重载支持，
        //所以这种比较是合法的，不需要显式调用 .string() 转换成 std::string 再比较。
        if(iter->path().extension().string()!=".html")
        {
            continue;
        }
        // std::cout<<iter->path().string()<<std::endl;
        files_list->push_back (iter->path().string());
    }
    return true;
}
static bool ParseTitle(const std::string&out,std::string* title)//解析出标题,out是从files_list读出来文件路径
{
    std::size_t start=out.find("<title>");
    if(start==std::string::npos)
    {
        return false;
    }
    std::size_t end = out.find("</title>");
    if(end == std::string::npos){
        return false;
    }
    start += std::string("<title>").size();
    if(start>end) return false;
    *title=out.substr(start,end-start);
    return true;
}
static bool ParseContent(const std::string&out,std::string* content)
{
    enum status//状态解析html，碰到>看是否到正文改变状态
    {
        LABLE,
        CONTENT
    };
    enum status s = LABLE;
    for(char c:out)
    {
        switch(s)
        {
            case LABLE:
                if(c=='>') s=CONTENT;
                break;
            case CONTENT:
                if(c=='<') s=LABLE;
                else
                {
                    //我们不想保留原始文件中的\n,因为我们想用\n作为html解析之后文本的分隔符
                    if(c == '\n') c = ' ';
                    content->push_back(c);
                }
                break;
            defalut:
            break;
        }
    }
    return true;
}//for debug
static void ShowDoc( const DocInfo_t &doc)
{
    std::cout << "title: " << doc.title << std::endl;
    std::cout << "content: " << doc.content << std::endl;
    std::cout << "url: " << doc.url << std::endl;
}
 static bool ParseUrl(const std::string&file_path,std::string* url)
{
    std::string url_head= "https://www.boost.org/doc/libs/1_89_0/doc/html";
    std::string url_tail=file_path.substr(src_path.size());
    *url=url_head+url_tail;
    return true;
}
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)//2
{
    for(const std::string file:files_list)
    {
        //1:读取文件，
        std::string out;
        if(!ns_util::FileUtil::ReadFile(file,&out))//3
        {
            continue;
        }
        DocInfo_t doc;
        //2，解析文件，提取title
        if(!ParseTitle(out,&doc.title))//4
        {
            continue;
        }

        //3,解析文件，提取content。去标签
        if(!ParseContent(out,&doc.content))//5
        {
            continue;
        }
        //4,解析文件路径，设置url,传路径，别的传内容
        if(!ParseUrl(file,&doc.url))//6
        {
            continue;
        }
        //到这已经完成可以写入
        results->push_back(doc);//bug:写时拷贝效率降低，后续改
        //测试是否提取成功
        // ShowDoc(doc);
        // break;
    }
    return true;
}
// const std::string output="data/raw_html/raw.txt";
bool SaveHtml(const std::vector<DocInfo_t>& results,const std::string&output)
{
#define SEP '\3'
    std::ofstream out(output,std::ios::out|std::ios::binary);
    if(!out.is_open())
    {
        std::cerr<<"open "<<output<<" failed";
        return false;
    }
    for(auto &it:results)
    {
        std::string out_string;
        out_string = it.title;
        out_string += SEP;
        out_string += it.content;
        out_string += SEP;
        out_string += it.url;
        out_string += '\n';
        out.write(out_string.c_str(), out_string.size());
    }
    return true;
}
// bool Sav(const std::string&adver)
// {
// #define SEP '\3'
//     std::ofstream out(adver,std::ios::out|std::ios::binary);
//     if(!out.is_open())
//     {
//         std::cerr<<"open "<<output<<" failed";
//         return false;
//     }
   
//         std::string out_string;
//         out_string = "boostfilesystem";
//         out_string += SEP;
//         out_string += "Boost 文件系统工具包";
//         out_string += SEP;
//         out_string += "提供跨平台文件系统操作，支持路径处理与文件管理，适配多系统环境";
//         out_string += SEP;
//         out_string += "https://boostfs.example.com";
//         out_string += SEP;
//         out_string += "10";
//         out_string += '\n';
//         out.write(out_string.c_str(), out_string.size());
//     return true;
// }